#pragma once

#include<string>
#include<memory>
#include<sstream>
#include<jsoncpp/json/json.h>

#define TCP_PORT 47000
#define TCP_DATA_MAX_LEN 65535

using std::string;

enum chat_msg_type
{
    Register = 0,          //0--注册请求
    Register_Resp,         //1--注册应答
    Login,                 //2--登录请求
    Login_Resp,            //3--登录应答
    AddFriend,             //4--添加好友请求
    AddFriend_Resp,        //5--添加好友应答
    SendMsg,               //6--发送消息
    SendMsg_Resp,          //7--发送消息应答
    PushMsg,               //8--推送消息
    PushMsg_Resp,          //9--推送消息应答
    PushAddFriendMsg,      //10--推送添加好友请求
    PushAddFriendMsg_Resp, //11--推送添加好友请求应答
    GetFriendMsg,          //12--获取全部好友信息
    GetFriendMsg_Resp,      //13--获取全部好友信息的应答
    SetUserOffLine          //14--将下线的用户状态置为离线
};


enum reply_status
{
    REGISTER_SUCCESS = 0, //0--注册成功
    REGISTER_FAILED,      //1--注册失败
    LOGIN_SUCCESS,        //2--登录成功
    LOGIN_FAILED,         //3--登录失败
    ADDFRIEND_SUCCESS,    //4--添加好友成功
    ADDFRIEND_FAILED,     //5--添加好友失败
    SENDMSG_SUCCESS,      //6--发送消息成功
    SENDMSG_FAILED,       //7--发送消息失败
    GETFRIEND_SUCCESS,    //8--获取好友列表成功
    GETFRIEND_FAILED      //9--获取好友列表失败
};

/*
 *组织不同类型的消息格式-----以此来约定通信双方对消息的读取方式
 *
 * 1、注册请求的消息格式
 *    sockfd_（消息到达服务端之后，由服务端接收之后，打上sockfd_）
 *    msg_type_:Register
 *    json_msg_:{
 *       nickname: "xxx"
 *       school: "xxx"
 *       telnum: "xxx"
 *       passwd: "xxx"
 *    }
 * 
 * 2、注册应答的消息格式
 *    msg_type_ : Register_Resp
 *    reply_status_: REGISTER_SUCCESS / REGISTER_FAILED
 *         如果是REGISTER_SUCCESS 需要返回user_id_
 *
 *
 * 3、登录请求的消息格式
 *    sockfd_:(消息到达服务端之后，由服务端接收之后，打上sockfd)
 *    msg_type_:Login
 *    json_msg_:{
 *      telnum: "xxx"
 *      passwd: "xxx"
 *    }
 *
 * 4、登录应答的消息格式
 *    msg_type_:Login_Resp
 *    reply_status_: LOGIN_SUCCESS / LOGIN_FAILED
 *         如果是REGISTER_SUCCESS 需要返回user_id_
 *
 *
 * 5、添加好友请求的消息格式
 *    msg_type_: AddFriend
 *    json_msg_: {
 *       fri_tel_num: "xxx"
 *    }
 *
 * 6、添加好友应答消息格式
 *    msg_type_:AddFriend_Resp
 *    reply_status_: ADDFRIEND_SUCCESS / ADDFRIEND_FAILED
 *          如果是ADDFRIEND_SUCCESS
 *              json_msg_: {
 *                  BeAdd_nickname:被添加方的昵称
 *                  BeAdd_school: 被添加方的学校
 *                  BeAdd_userid: 被添加方的id
 *              }
 *
 * 7、推送添加好友的请求
 *    msg_type_: PushAddFriendMsg
 *    sockfd_: 被添加方的套接字描述符
 *    json_msg_: {
 *       adder_nickname: "xxx"
 *       adder_school: "xxx"
 *       addr_userid: "xxx"
 *    }
 *
 * 8、推送添加好友的应答（被添加方给服务端的消息）
 *    msg_type_:PushAddFriendMsg_Resp
 *    user_id_: 被添加方的id
 *    reply_status_: ADDFRIEND_SUCCESS / ADDFRIEND_FAILED
 *    json_msg_：添加方的id
 * */


/*
 *  封装json提供的序列化与反序列化接口
 *
 * */
class JsonUtil
{
    public:
        //json对象的序列化---传入一个json对象，将其序列化为一个string对象，以出参的形式返回
        static bool Serialize(const Json::Value& value, string* body)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

            std::stringstream ss;
            int ret = sw->write(value, &ss);
            if(ret != 0)
            {
                return false;
            }
            *body = ss.str();
            return true;
        }
        //json对象的反序列化--传入一个字符串，将其反序列化为一个json对象，以出参的形式返回
        static bool UnSerialize(const string& body, Json::Value* value)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

            string err;
            bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), value, &err);
            if(ret == false)
            {
                return false;
            }
            return true;
        }
};


class ChatMsg
{
    public:
        ChatMsg()
        {
            sockfd_ = -1;
            msg_type_ = -1;
            user_id_ = -1;
            reply_status_ = -1;
            json_msg_.clear();
        }
        ~ChatMsg()
        {}

        /*
         * 提供反序列化接口，接收完后进行反序列化
         * */
        int PraseMsg(int sockfd, const string& msg)
        {
            //1、调用json的反序列化接口
            Json::Value tmp;
            bool ret = JsonUtil::UnSerialize(msg, &tmp);
            if(ret == false)
            {
                return -1;
            }
            //2、对成员变量进行赋值
            sockfd_ = sockfd;
            msg_type_ = tmp["msg_type"].asInt();
            reply_status_ = tmp["reply_status"].asInt();
            user_id_ = tmp["user_id"].asInt();
            json_msg_ = tmp["json_msg"];
            return 0;
        }
        /*
         * 提供序列化接口，用于回复应答
         *   msg：出参，保存序列化后的结果
         * */

        bool GetMsg(string* msg)
        {
            //1、调用json的序列化接口
            Json::Value tmp;
            tmp["msg_type"] = msg_type_;
            tmp["reply_status"] = reply_status_;
            tmp["user_id"] = user_id_;
            tmp["json_msg"] = json_msg_;

            return JsonUtil::Serialize(tmp ,msg);
        }
        /*
         * 获取json_msg_中的value值
         * */ 
        string GetValue(const string& key)
        {
            if(!json_msg_.isMember(key))
            {
                return "";
            }
            return json_msg_[key].asString();
        }

        /*
         * 设置json_msg_中的k-v键值对
         * */
        void SetValue(const string& key, const string& value)
        {
            json_msg_[key] = value;
        }

        void SetValue(const string& key, int value)
        {
            json_msg_[key] = value;
        }

        void Clear()
        {
            /*注意：千万不要把套接字描述符清理为-1，否则就无法send成功了！！*/
            msg_type_ = -1;
            user_id_ = -1;
            reply_status_ = -1;
            json_msg_.clear();
        }

    public:
        //客户端的套接字描述符
        int sockfd_;
        //消息类型
        int msg_type_;
        /* 
         *可选项有：
         *
         *   Register = 0,          //0--注册请求
         *   Register_Resp,         //1--注册应答
         *   Login,                 //2--登录请求
         *   Login_Resp,            //3--登录应答
         *   AddFriend,             //4--添加好友请求
         *   AddFriend_Resp,        //5--添加好友应答
         *   SendMsg,               //6--发送消息
         *   SendMsg_Resp,          //7--发送消息应答
         *   PushMsg,               //8--推送消息
         *   PushMsg_Resp,          //9--推送消息应答
         *   PushAddFriendMsg,      //10--推送添加好友请求
         *   PushAddFriendMsg_Resp, //11--推送添加好友请求应答
         *   GetFriendMsg,          //12--获取全部好友信息
         *   GetFriendMsg_Resp      //13--获取全部好友信息的应答
         *
         * */

        //客户端自己的用户id
        int user_id_;

        //应答状态
        int reply_status_;
        /*
         *可选项有：
         *
         *   REGISTER_SUCCESS = 0, //0--注册成功
         *   REGISTER_FAILED,      //1--注册失败
         *   LOGIN_SUCCESS,        //2--登录成功
         *   LOGIN_FAILED,         //3--登录失败
         *   ADDFRIEND_SUCCESS,    //4--添加好友成功
         *   ADDFRIEND_FAILED,     //5--添加好友失败
         *   SENDMSG_SUCCESS,      //6--发送消息成功
         *   SENDMSG_FAILED,       //7--发送消息失败
         *   GETFRIEND_SUCCESS,    //8--获取好友列表成功
         *   GETFRIEND_FAILED      //9--获取好友列表失败
         *
         * */


        //消息
        Json::Value json_msg_;
};

